/**
 * <p>Copyright (R) 2014 正方软件股份有限公司。<p>
 */
package com.zfsoft.controller;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.nio.channels.FileLock;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author 魏广跃（1571）
 *  默认临时文件构造器
 */
@Component
public class DefaultTempFileBuilder implements TempFileBuilder {

	private static final Logger log = LoggerFactory.getLogger(DefaultTempFileBuilder.class);
	// 工作目录内文件暂留7天
	protected static final long TEMP_FILE_KEEP_ALIVE_DAY = 3;
	// 单位换算
	protected static final long TEMP_FILE_KEEP_ALIVE_TIME = TEMP_FILE_KEEP_ALIVE_DAY /* 天 */ * 24/* 时 */ * 60/* 分 */	* 60 /* 秒 */ * 1000/* 毫秒 */;
	// 临时文件目录
	protected File userDir = FileUtils.getUserDirectory();
	// 公司临时文件工作根目录
	protected String tempFileRootDirName = "zfsoft";
	// 清理线程调度周期，1小时
	protected static final int CLEANER_SCHEDULE_INTERVAL_HOUR = 1;
	// 执行器
	protected ScheduledExecutorService cleanerScheduledExecutor;
	//临时文件跟目录
	private File tempFileRootDir;
	//清理锁文件名称
	private String cleanLockFileName = "cleaner.lock";
	
	private RandomAccessFile  cleanLockFile;

	private FileNameGenerator fileNameGenerator = new FileNameGenerator();
	
	// 当初始化
	@PostConstruct
	public void init() {
		try {
			this.doInit();
		} catch (Throwable t) {
			log.error("初始化工作目录[{}]异常",this.tempFileRootDir,t);
		}
	}

	protected void doInit() {

		this.tempFileRootDir = new File(userDir, tempFileRootDirName);
		if (this.tempFileRootDir.exists()) {
			log.info("使用公司临时文件工作根目录:{}", this.tempFileRootDir);
		} else {
			log.info("初始化创建公司临时文件工作根目录:{}", this.tempFileRootDir);
			this.tempFileRootDir.mkdirs();
		}
		File lockFile = new File(this.tempFileRootDir,cleanLockFileName);
		if(!lockFile.exists()) {
			try {
				lockFile.createNewFile();
			} catch (IOException e) {
				log.error("",e);
			}
		}
		
		try {
			this.cleanLockFile = new RandomAccessFile(lockFile, "rw");
		} catch (FileNotFoundException e) {
			log.error("",e);
		}

		// 线程执行器
		final String cleanerThreadNamePrefix = DefaultTempFileBuilder.class.getSimpleName() + "_workDir_cleaner_"+ tempFileRootDir.toString();
		this.cleanerScheduledExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
			@Override
			public Thread newThread(Runnable r) {
				Thread newThread = new Thread(r, cleanerThreadNamePrefix);
				return newThread;
			}
		});
		// 周期调度任务
		this.cleanerScheduledExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				DefaultTempFileBuilder.this.cleanModularDirs();
			}
		}, CLEANER_SCHEDULE_INTERVAL_HOUR, CLEANER_SCHEDULE_INTERVAL_HOUR, TimeUnit.HOURS);
		
		this.cleanModularDirs();
	}
	
	// 当销毁
	@PreDestroy
	public void destory() {
		this.cleanerScheduledExecutor.shutdown();
		//销毁再执行一次清理,用于开发测试
		this.cleanModularDirs();
	}

	protected void cleanModularDirs() {
		try {
			FileLock lock = this.cleanLockFile.getChannel().tryLock();
			if(lock != null) {
				File[] moduleDirs = this.tempFileRootDir.listFiles();
				for(File dir: moduleDirs) {
					if(dir.isDirectory()) {
						try {
							this.doCleanModularDir(dir);
						} catch (Throwable t) {
							log.error("清理工作目录[{}]异常",this.tempFileRootDir,t);
						}
					}else {
						//
					}
				}
				lock.release();
			}else {
				//执行到这里，说明其他的webapp实例已经在做这个动作了,我们也就不必做了
				//冲突避让
			}
		} catch (IOException e) {
			log.error("",e);
		}
	}

	/**
	 * tomcat的reload会触发spring的reload,但session不会清除,所以只能采取删除一定时间之前的工作文件
	 * 其实最佳删除周期,是session过期周期,但这里7天也是为了统计遗留的文件个数，方便排查问题
	 */
	protected void doCleanModularDir(File dir) {
		File[] files = dir.listFiles();
		if (files != null) {
			long now = System.currentTimeMillis();
			for (int i = 0; i < files.length; i++) {
				File f = files[i];
				if (f.isFile()) {
					long lastModefied = f.lastModified();
					if (now - lastModefied > TEMP_FILE_KEEP_ALIVE_TIME) {
						f.delete();
					}
				}
			}
		}
	}

	/**
	 * @description ： 创建临时工作文件
	 * @param moduleDir
	 *            所属模块的字符串,可以使用调用此方法的所在类的getSimpleName获得
	 * @param suffix
	 *            文件后缀
	 * @return
	 * @throws IOException 
	 */
	public File createTempFile(String moduleDir, String suffix){
		if(moduleDir == null || "".equals(moduleDir.trim())) {
			throw new IllegalArgumentException("moduleDir can't be null or ''");
		}
		if(suffix == null || "".equals(suffix.trim())) {
			throw new IllegalArgumentException("suffix can't be null or ''");
		}
		String newFileName = fileNameGenerator.next() +"."+ suffix;
		
		File moduleDirFile = new File(this.tempFileRootDir,moduleDir);
		if(!moduleDirFile.exists()) {
			moduleDirFile.mkdir();
		}
		File newFile = new File(moduleDirFile,newFileName);
		try {
			newFile.createNewFile();
		} catch (IOException e) {
			log.error("创建文件异常{}",newFile,e);
		}
		return newFile;
	}
	
	/**
	 * @author 		：魏广跃（1571）
	 * 创建临时文件名称
	 * 格式：uuid-这个jvm的pid-这个web应用的生成序号,这样方便排查是哪个jvm产生的临时文件
	 */
	private static class FileNameGenerator{
		
		private int no = 0;
		private String pid = "";
		
		public FileNameGenerator() {
			try {
				String name = ManagementFactory.getRuntimeMXBean().getName();
				this.pid = name.split("@")[0];
			}catch(Exception e) {
				log.error("",e);
			}
		}
		
		public synchronized String next() {
			String startUuid = UUID.randomUUID().toString();
			String next = String.format("%s-%s-%s", startUuid,this.pid,this.no);
			this.no ++;
			return next;
		}
	}
}
